import { useUserStore } from '/@/store/modules/system/user'
import { some } from 'lodash'
import { message } from 'ant-design-vue'

/**
 * 构造树型结构数据
 * @param {*} data 数据源
 * @param {*} id id字段 默认 'id'
 * @param {*} parentId 父节点字段 默认 'parentId'
 * @param {*} children 孩子节点字段 默认 'children'
 */
export function handleTree(data, id, parentId, children) {
  const config = {
    id: id || 'id',
    parentId: parentId || 'parentId',
    childrenList: children || 'children',
  }

  const childrenListMap = {}
  const nodeIds = {}
  const tree = []

  for (const d of data) {
    const parentId = d[config.parentId]
    if (childrenListMap[parentId] == null) {
      childrenListMap[parentId] = []
    }
    nodeIds[d[config.id]] = d
    childrenListMap[parentId].push(d)
  }

  for (const d of data) {
    const parentId = d[config.parentId]
    if (nodeIds[parentId] == null) {
      tree.push(d)
    }
  }

  for (const t of tree) {
    adaptToChildrenList(t)
  }

  function adaptToChildrenList(o) {
    if (childrenListMap[o[config.id]] !== null) {
      o[config.childrenList] = childrenListMap[o[config.id]]
    }
    if (o[config.childrenList]) {
      for (const c of o[config.childrenList]) {
        adaptToChildrenList(c)
      }
    }
  }
  return tree
}

// 树形数据结构扁平化
export function flattenTree(tree, childField = 'child') {
  const result = []

  function flatten(node, childField) {
    node.forEach((item) => {
      let res = JSON.parse(JSON.stringify(item)) // 先克隆一份数据作为第一层级的填充
      delete res[childField]
      result.push(res)
      if (item[childField] && item[childField].length > 0) {
        flatten(item[childField], childField)
      }
    })
  }

  flatten(tree, childField)
  return result
}

/**
 * 通用过滤方法
 * @param {Object|Array} input - 输入的对象或数组
 * @param {Function} predicate - 过滤条件函数
 * @returns {Object|Array} - 返回过滤后的对象或数组
 */
export function deepFilter(input, predicate) {
  if (Array.isArray(input)) {
    return input
      .map((item) => {
        if (Array.isArray(item) || (typeof item === 'object' && item !== null)) {
          return deepFilter(item, predicate) // 递归过滤
        }
        return item
      })
      .filter(predicate)
  } else if (typeof input === 'object' && input !== null) {
    return Object.entries(input).reduce((acc, [key, value]) => {
      if (Array.isArray(value) || (typeof value === 'object' && value !== null)) {
        acc[key] = deepFilter(value, predicate) // 递归过滤
      } else if (predicate(value, key)) {
        acc[key] = value
      }
      return acc
    }, {})
  } else {
    throw new TypeError('Input must be an object or array')
  }
}
export function myfilter(input, predicate) {
  if (Array.isArray(input)) {
    // 如果是数组，使用 filter 方法
    return input.filter(predicate)
  } else if (typeof input === 'object' && input !== null) {
    // 如果是对象，使用 Object.entries 和 reduce 方法
    return Object.entries(input).reduce((acc, [key, value]) => {
      if (predicate(value, key)) {
        acc[key] = value
      }
      return acc
    }, {})
  } else {
    throw new TypeError('Input must be an object or array')
  }
}

/**
 * 将数字四舍五入到指定小数位，并移除结果末尾的零。
 * 如果结果以小数点结尾，也会移除该小数点。
 *
 * @param number 需要进行四舍五入的数字。
 * @param digits 指定保留的小数位数。
 * @return 返回四舍五入后的字符串，移除了末尾的零和可能的小数点。
 */
export function myToFixed(number, digits = 2) {
  // 检查输入是否为有效数字
  if (number === null || number === undefined || isNaN(number) || !isFinite(number)) {
    return undefined
  }

  let result = number.toFixed(digits)
  while (result.endsWith('0')) {
    result = result.slice(0, -1)
  }
  if (result.endsWith('.')) {
    result = result.slice(0, -1)
  }
  return result
}
// 将附件文件路径转换为文件对象数组
export const convertPathsToFiles = (pathArr = [], nameArr = []) => {
  // 工具函数：将字符串转换为数组（以逗号分隔）
  const toArray = (input) => (typeof input === 'string' ? input.split(',').map((s) => s.trim()) : input)

  const normalizedPaths = toArray(pathArr)
  let normalizedNames = toArray(nameArr)

  // 如果 names 长度不一致，使用路径作为默认名称或加索引后缀
  if (normalizedNames.length !== normalizedPaths.length) {
    normalizedNames = normalizedPaths.map((_, index) => (normalizedNames[0] ? `${normalizedNames[0]}_${index + 1}` : normalizedPaths[index]))
  }

  return normalizedPaths.map((path, index) => ({
    uid: `db-file-${index}`,
    name: normalizedNames[index],
    status: 'done',
    url: path,
  }))
}

const getValueOptions = (selectedValue, options) => {
  if (!selectedValue) return null // 如果没有选中值，返回 null 或其他默认值
  // 单选模式：返回单一标签值
  const option = options.find((opt) => opt.value === selectedValue)
  return option ? option : '未匹配'
}
// 方法：获取并更新选中的标签
export const getSelectedOptions = (innerValue, childComponent) => {
  const options = childComponent.value?.options || []
  return getValueOptions(innerValue, options)
}

/**
 * 通过点号分隔的路径访问嵌套对象的属性 获取深层对象值
 * @param {Object} obj - 要访问的对象 例如 "obj:{a:{aa:{aaa:111}}}"
 * @param {string} path - rule分隔的路径，例如 "a.aa.aaa"
 * @param {string} rule - 路径分隔符号，例如 '.'
 * @returns {*} - 返回对应的属性值，如果路径无效则返回 undefined
 */
export function getObjValue(obj, path, rule = '.') {
  return path.split(rule).reduce((acc, key) => {
    return acc && acc[key] !== undefined ? acc[key] : undefined
  }, obj)
}
/**
 * 通过点号分隔的路径给嵌套对象的属性赋值
 * @param {Object} obj - 要赋值的对象 例如 "obj:{a:{aa:{aaa:111}}}"
 * @param {string} path - rule分隔的路径，例如 "a.aa.aaa"
 * @param {string} value - 要赋的值
 * @param {string} rule - 路径分隔符号，例如 '.'
 * @returns {*} - 返回对应的属性值，如果路径无效则返回 undefined
 */
export function setObjValue(obj, path, value, rule = '.') {
  const keys = path.split(rule)
  let current = obj

  for (let i = 0; i < keys.length - 1; i++) {
    const key = keys[i]
    if (!current[key]) {
      current[key] = {}
    }
    current = current[key]
  }

  current[keys[keys.length - 1]] = value
}

/**
 * string转array
 */
export function strToArr(val, rule = ',') {
  // 处理 null 或 undefined
  if (val === null || val === undefined) {
    return []
  }

  // 如果已经是数组，直接返回
  if (Array.isArray(val)) {
    return val
  }

  // 判断是否为基本类型
  const type = typeof val

  if (type === 'number') {
    if (isNaN(val)) {
      return [] // 或者 throw new TypeError('NaN is not allowed');
    }
    val = val.toString()
  } else if (type === 'boolean') {
    val = val.toString()
  } else if (type !== 'string') {
    // 非字符串/数字/布尔值等非基础类型，抛出错误或返回空
    return [] // 或者 throw new TypeError(`Unsupported type: ${type}`);
  }

  // 确保 rule 是字符串或正则表达式
  if (typeof rule !== 'string' && !(rule instanceof RegExp)) {
    rule = ','
  }

  return val.split(rule)
}

// 日期格式化
export function parseTime(time, pattern) {
  if (arguments.length === 0 || !time) {
    return null
  }
  const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
  let date
  if (typeof time === 'object') {
    date = time
  } else {
    if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
      time = parseInt(time)
    } else if (typeof time === 'string') {
      time = time
        .replace(new RegExp(/-/gm), '/')
        .replace('T', ' ')
        .replace(new RegExp(/\.[\d]{3}/gm), '')
    }
    if (typeof time === 'number' && time.toString().length === 10) {
      time = time * 1000
    }
    date = new Date(time)
  }
  const formatObj = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay(),
  }
  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
    let value = formatObj[key]
    // Note: getDay() returns 0 on Sunday
    if (key === 'a') {
      return ['日', '一', '二', '三', '四', '五', '六'][value]
    }
    if (result.length > 0 && value < 10) {
      value = '0' + value
    }
    return value || 0
  })
  return time_str
}

// 回显数据字典
export function selectDictLabel(datas, value, fieldNames) {
  fieldNames = { value: 'value', label: 'desc', ...fieldNames }
  if (value === undefined) {
    return ''
  }
  const actions = []
  Object.keys(datas).some((key) => {
    if (datas[key][fieldNames.value] == '' + value) {
      actions.push(datas[key][fieldNames.label])
      return true
    }
  })
  if (actions.length === 0) {
    actions.push(value)
  }
  return actions.join('')
}

// 回显数据字典（字符串数组）
export function selectDictLabels(datas, value, option) {
  option = { separator: ',', fieldNames: { value: 'value', label: 'label', ...option?.fieldNames }, ...option }
  const { separator, fieldNames } = option
  if (value === undefined || value.length === 0) {
    return ''
  }
  if (Array.isArray(value)) {
    value = value.join(',')
  }
  const actions = []
  const currentSeparator = undefined === separator ? ',' : separator
  const temp = value.split(currentSeparator)
  Object.keys(value.split(currentSeparator)).some((val) => {
    let match = false
    Object.keys(datas).some((key) => {
      //@ts-ignore
      if (datas[key][fieldNames.value] == '' + temp[val]) {
        // @ts-ignore
        actions.push(datas[key][fieldNames.label] + currentSeparator)
        match = true
      }
    })
    if (!match) {
      // @ts-ignore
      actions.push(temp[val] + currentSeparator)
    }
  })
  return actions.join('').substring(0, actions.join('').length - 1)
}

/**
 * 是否有权限
 * @param {string,Array}  字符串或数组
 * @returns {Boolean} 布尔类型
 */
export function privilege(val) {
  if (isEmpty(val)) {
    return true
  }
  // 超级管理员
  if (useUserStore().superFlag) {
    return true
  }
  // 获取功能点权限
  let userPointsList = useUserStore().getPointList
  if (!userPointsList) {
    return false
  }
  // 判断是否有权限
  if (!some(userPointsList, ['permission', val])) {
    return false
  }
  return true
}
// 秒数转换
export function formatTime(seconds) {
  if (typeof seconds !== 'number' || seconds < 0) {
    throw new Error('请输入有效的非负秒数')
  }

  const mins = Math.floor(seconds / 60) // 计算分钟数
  const secs = seconds % 60 // 计算剩余的秒数

  // 使用 padStart 确保分钟和秒数都是两位数
  return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
}

/**
 * 复制内容到剪贴板
 * @param {string} content - 需要复制的内容
 */
export function copy(content) {
  // 创建元素用于复制
  const aux = document.createElement('input')
  // 设置元素内容
  aux.setAttribute('value', content)
  // 将元素插入页面进行调用
  document.body.appendChild(aux)
  // 复制内容
  aux.select()
  // 将内容复制到剪贴板
  document.execCommand('copy')
  // 删除创建元素
  document.body.removeChild(aux)

  message.success('复制成功')
}

// MIME类型映射表（可根据需要扩展）
const mimeTypeMap = {
  '.txt': 'text/plain',
  '.html': 'text/html',
  '.css': 'text/css',
  '.js': 'application/javascript',
  '.json': 'application/json',
  '.xml': 'application/xml',
  '.pdf': 'application/pdf',
  '.doc': 'application/msword',
  '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  '.xls': 'application/vnd.ms-excel',
  '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  '.ppt': 'application/vnd.ms-powerpoint',
  '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  '.png': 'image/png',
  '.jpg': 'image/jpeg',
  '.jpeg': 'image/jpeg',
  '.gif': 'image/gif',
  '.bmp': 'image/bmp',
  '.svg': 'image/svg+xml',
  '.webp': 'image/webp',
  '.mp3': 'audio/mpeg',
  '.wav': 'audio/wav',
  '.mp4': 'video/mp4',
  '.avi': 'video/x-msvideo',
  '.mov': 'video/quicktime',
  '.wmv': 'video/x-ms-wmv',
  '.webm': 'video/webm',
  '.ogg': 'application/ogg',
}

/**
 * 根据Base64编码的字符串下载文件
 *
 * @param {string} base64String - Base64编码的字符串
 * @param {string} filename - 下载文件时使用的文件名
 * @param {string} [mimeType='application/octet-stream'] - 文件的MIME类型，默认为'application/octet-stream'
 */
export function downloadBase64File(base64String, filename, mimeType = 'application/octet-stream') {
  if (typeof base64String !== 'string' || !filename) {
    console.error('输入参数无效')
    return
  }

  // 自动识别 MIME 类型
  if (mimeType === 'application/octet-stream') {
    const lastDotIndex = filename.lastIndexOf('.')
    const fileExtension = lastDotIndex > 0 ? filename.slice(lastDotIndex).toLowerCase() : ''
    mimeType = mimeTypeMap[fileExtension] || mimeType
  }

  let url = null
  let a = null

  try {
    // 提取 Base64 数据
    const base64Data = base64String.split(',')[1] || base64String

    // 解码 Base64
    const byteCharacters = atob(base64Data)

    // 创建 Uint8Array
    const byteArray = new Uint8Array(byteCharacters.length)
    for (let i = 0; i < byteCharacters.length; i++) {
      byteArray[i] = byteCharacters.charCodeAt(i)
    }

    // 创建 Blob
    const blob = new Blob([byteArray], { type: mimeType })

    // 创建 URL
    url = URL.createObjectURL(blob)

    // 创建并配置 <a> 标签
    a = document.createElement('a')
    a.href = url
    a.download = filename
    document.body.appendChild(a)

    // 触发下载
    a.click()
  } catch (error) {
    console.error('Failed to download file:', error)
  } finally {
    // 清理资源
    if (a) {
      document.body.removeChild(a)
    }
    if (url) {
      URL.revokeObjectURL(url)
    }
  }
}

/**
 * 将数字格式化为金额字符串
 *
 * @param {number|string} num - 要格式化的数字
 * @param {boolean} [showDecimal=true] - 是否显示小数部分，默认显示
 * @returns {string} 格式化后的金额字符串
 */
export function formatToMoney(num, showDecimal = true) {
  // 确保输入为数字或可转换为数字的字符串
  if (typeof num === 'string') {
    num = parseFloat(num)
    if (isNaN(num)) return '0' + (showDecimal ? '.00' : '')
  }

  if (typeof num !== 'number' || isNaN(num)) return '0' + (showDecimal ? '.00' : '')

  // 分离整数和小数部分
  const [integerPart, decimalPart] = num.toFixed(showDecimal ? 2 : 0).split('.')

  // 使用正则表达式添加千分位逗号
  const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',')

  // 拼接结果
  return showDecimal ? `${formattedInteger}.${decimalPart}` : formattedInteger
}

/**
 * 递归修改对象/数组中指定字段的值
 * @param {Object|Array} data - 要处理的数据（对象或数组）
 * @param {Array<string>} fields - 要修改的字段名数组
 * @param {Function} modifyFn - 修改函数，接收原值并返回新值
 */
export function modifyFields(data, fields, modifyFn) {
  if (Array.isArray(data)) {
    data.forEach((item) => modifyFields(item, fields, modifyFn))
  } else if (typeof data === 'object' && data !== null) {
    for (let key in data) {
      if (Object.prototype.hasOwnProperty.call(data, key)) {
        if (fields.includes(key)) {
          data[key] = modifyFn(data[key], key)
        } else {
          modifyFields(data[key], fields, modifyFn) // 递归处理子对象
        }
      }
    }
  }
  return data
}

/**
 * 深度比较两个对象是否相等
 * @param {*} a - 第一个比较对象
 * @param {*} b - 第二个比较对象
 * @returns {boolean} - 返回比较结果，true表示相等，false表示不等
 */
export function deepEqual(a, b, cache = new WeakMap()) {
  if (a === b) return true

  // Handle circular references
  if (cache.has(a) && cache.has(b)) {
    return cache.get(a) === cache.get(b)
  }
  cache.set(a, b)
  cache.set(b, a)

  if (typeof a !== 'object' || typeof b !== 'object' || a == null || b == null) return false

  // Handle special objects
  if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime()
  if (a instanceof RegExp && b instanceof RegExp) return a.toString() === b.toString()
  if (a instanceof Set && b instanceof Set) return deepEqual([...a], [...b], cache)
  if (a instanceof Map && b instanceof Map) return deepEqual([...a], [...b], cache)

  const keysA = Object.getOwnPropertyNames(a)
  const keysB = Object.getOwnPropertyNames(b)

  if (keysA.length !== keysB.length) return false

  for (let key of keysA) {
    if (!Object.prototype.hasOwnProperty.call(b, key)) return false
    if (!deepEqual(a[key], b[key], cache)) return false
  }

  return true
}
/**
 * 判断是否为空值（null、undefined、空字符串、空对象、空数组）
 * @param {*} value
 * @returns {boolean}
 */
export const isEmpty = (value) => {
  // null 或 undefined
  if (value === null || typeof value === 'undefined') {
    return true
  }

  // 数字 0、NaN 不认为是空
  if (typeof value === 'number' && !isNaN(value)) {
    return false
  }

  // 布尔值：false 不认为是空
  if (typeof value === 'boolean') {
    return false
  }

  // 字符串：仅空格也认为是空
  if (typeof value === 'string') {
    return value.trim() === ''
  }

  // 数组或对象
  if (typeof value === 'object') {
    // 数组
    if (Array.isArray(value)) {
      return value.length === 0
    }
    // 对象
    return Object.keys(value).length === 0
  }

  // 其他情况（如 function）不认为是空
  return false
}
/**
 * 通用脱敏方法
 * @param {string} str - 需要脱敏的字符串
 * @param {number} start - 前面保留的字符数
 * @param {number} end - 后面保留的字符数
 * @param {string} mask - 遮盖字符
 * @returns {string} 脱敏后的字符串
 */
export function desensitizeString(str, start, end, mask = '*') {
  if (!str) return ''
  if (!start && !end) {
    let strLength = str.length
    start = Math.floor(strLength / 3)
    end = Math.floor(strLength / 3)
  }

  const length = str.length
  const showStart = Math.min(start, length)
  const showEnd = Math.min(end, length - showStart)

  if (showStart + showEnd >= length) {
    return str
  }

  const maskLength = length - showStart - showEnd
  return str.substring(0, showStart) + mask.repeat(maskLength) + str.substring(length - showEnd)
}
